using Wilco.SyntaxHighlighting.Engine.Node;
using Wilco.SyntaxHighlighting.Engine.Node.Implementation.Xml;
using Wilco.SyntaxHighlighting.Engine.Node.Occurrence;
using Wilco.SyntaxHighlighting.Engine.Scanner;
using Wilco.SyntaxHighlighting.Engine.Scanner.Implementation;
using Wilco.SyntaxHighlighting.Engine.Tokenizer;

namespace Wilco.SyntaxHighlighting.Engine.Scanner.Implementation.PHP
{
    /// <summary>
    /// Represents a php scanner.
    /// </summary>
    public class PHPScanner : ScannerBase
    {
        private readonly OccurrenceCollection scannerResult;
        private readonly XmlScanner xmlScanner;
        private bool inScriptBlock;

        /// <summary>
        /// Initializes a new instance of a <see cref="PHPScanner"/> class.
        /// </summary>
        public PHPScanner() : base(null, null)
        {
            //
        }

        /// <summary>
        /// Initializes a new instance of a <see cref="PHPScanner"/> class.
        /// </summary>
        /// <param name="tokenizer">The <see cref="TokenizerBase"/> which is used to tokenize the source code.</param>
        /// <param name="scannerResult">The <see cref="OccurrenceCollection"/> which will contain the scanner result.</param>
        public PHPScanner(TokenizerBase tokenizer, OccurrenceCollection scannerResult) : base(tokenizer, scannerResult)
        {
            this.scannerResult = scannerResult;
            xmlScanner = new XmlScanner(Tokenizer, this.scannerResult);
            xmlScanner.Match += this.xmlScanner_Match;
            SetID("PHPScanner");
        }

        /// <summary>
        /// Initializes a new instance of a <see cref="IScanner"/> implementation class.
        /// </summary>
        /// <param name="tokenizer">The <see cref="TokenizerBase"/> which is used to tokenize the source code.</param>
        /// <param name="scannerResult">The <see cref="OccurrenceCollection"/> which will contain the scanner result.</param>
        /// <returns>A new instance of a <see cref="IScanner"/> implementation class.</returns>
        public override IScanner Create(TokenizerBase tokenizer, OccurrenceCollection scannerResult)
        {
            return new PHPScanner(tokenizer, scannerResult);
        }

        /// <summary>
        /// Scans a token.
        /// </summary>
        /// <remarks>
        /// An <see cref="IScanner"/> implementation will generally have a reference to a 
        /// <see cref="NodeCollection"/> which will be used to store results of a scan.
        /// </remarks>
        /// <param name="token">A token from the source code.</param>
        public override void Scan(string token)
        {
            if (!Enabled)
            {
                if (Child != null)
                {
                    Child.Scan(token);
                }
            }
            else
            {
                // Highlight as XML until a script block was found.
                if (!inScriptBlock)
                {
                    // Find script block.
                    xmlScanner.Scan(token);
                }
                else
                {
                    if ((Tokenizer.Position + 2) <= Tokenizer.Source.Length && Tokenizer.GetNextTokens(2) == "?>")
                    {
                        inScriptBlock = false;
                        xmlScanner.Scan(token);
                    }
                    else if (Child != null)
                        Child.Scan(token);
                }
            }
        }

        /// <summary>
        /// Handles the match event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void xmlScanner_Match(object sender, MatchEventArgs e)
        {
            if (e.Type == MatchType.StartIdentifier)
            {
                inScriptBlock = true;
            }
            else if (e.Type == MatchType.EndIdentifier)
            {
                inScriptBlock = false;
            }
        }

        /// <summary>
        /// Resets the scanner.
        /// </summary>
        public override void Reset()
        {
            inScriptBlock = false;
        }

    }
}